﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "NewInPlaceFamDlg.h"
#include "IUiView.h"
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "UniIdentity.h"
#include "IModelView.h"
#include "ICategoryLibrary.h"
#include "ICategory.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "IGenericElement.h"
#include "ILevel.h"
#include "UiDocumentViewUtils.h"
#include "IFamilyConfigElement.h"
#include "IElementBasicInformation.h"
#include "LevelUtils.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    std::wstring CreateUniqueInPlaceFamDocName(const IDocument* pDoc, const std::wstring& docNameCandidate)
    {
        DBG_WARN_AND_RETURN_UNLESS(pDoc != nullptr, L"", L"pDoc指针为空",L"GDMPLab",L"2024-03-30");

        std::wstring docName = docNameCandidate;
        int index = 0;

        do
        {
            std::wostringstream oss;
            oss << docNameCandidate << ++index;
            docName = oss.str();
        } while (IFamilyConfigElement::GetFamilyConfigElementByName(pDoc, docName));

        return docName;
    }
}
//////////////////////////////////////////////////////////////////////////

NewInPlaceFamDlgExchangeData::NewInPlaceFamDlgExchangeData()
{
    UniIdentity uniIdentity;
    CategoryUid = uniIdentity;
}

void NewInPlaceFamDlgExchangeData::SaveToString(std::wstring* pStr)
{
    if (!pStr)
    {
        return;
    }

    *pStr += FamName;

    *pStr += L",";

    *pStr += CategoryUid.GetGuidAndDebugString();
}

void NewInPlaceFamDlgExchangeData::LoadFromString(const std::wstring& str)
{
    std::wistringstream ss(str);
    ss >> FamName;

    std::wstring dummy;
    ss >> dummy;
    GBMP_ASSERT(dummy == L",");

    std::wstring categoryUidStr;
    ss >> categoryUidStr;

    if (categoryUidStr.length() > 37)
    {
        std::wstring guidString = categoryUidStr.substr(0, 38);
        gcmp::UniIdentity CategoryUid = gcmp::UniIdentity::Create(gcmp::GuidUtils::FromString(guidString), CategoryUid.GetDebugString());
    }

}

//////////////////////////////////////////////////////////////////////////
namespace
{
    struct CategoryCellData
    {
        const UniIdentity UniIdentity;
        const std::wstring IconPath;

        CategoryCellData(const gcmp::UniIdentity& uniIdentity, const std::wstring& iconPath)
            : UniIdentity(uniIdentity), IconPath(iconPath)
        {
        }
    };

    static std::vector<CategoryCellData> s_categoriesCellData;

    static int FindIndexOfCategory(const UniIdentity& uid)
    {
        for (int loop = 0; loop < (int)s_categoriesCellData.size(); ++loop)
        {
            if (uid == s_categoriesCellData[loop].UniIdentity)
            {
                return loop;
            }
        }
        return -1;
    }

    void InitCategoryCellDataForDisplay()
    {
        s_categoriesCellData.emplace_back(CategoryCellData(BuiltInCategoryUniIdentities::BICU_POINT_FAMILY, L":/images/点式族.png"));
    }

    ONE_TIME_INITIALIZER(InitCategoryCellDataForDisplay);
}

//////////////////////////////////////////////////////////////////////////
// 需要记住用户上次选择的类别
static UniIdentity s_lastCategoryUid;

NewInPlaceFamDlg::NewInPlaceFamDlg(const IDocument* pDoc, const ElementId currentLevelId, QWidget * pParent)
    : QDialog(pParent), m_categoryTableColumnCount(s_categoryTableColumnCount)
{
    ui.setupUi(this);

    this->setWindowTitle(QString::fromStdWString(GBMP_TR(L"新建在位构件")));
    ui.famNameLineEdit->setVisible(true);
    ui.famNameLineEdit->setText(QString::fromStdWString(CreateUniqueInPlaceFamDocName(pDoc, GBMP_TR(L"新构件"))));

    // 工作楼层ComboBox设置
    std::vector<IElement*> elementsOfLevelCategory = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_ELEVATION);
    FOR_EACH(pElement, elementsOfLevelCategory)
    {
        if (LevelUtils::GetLevelById(pDoc, pElement->GetElementId()))
        {
            ui.levelComboBox->addItem(QString::fromStdWString(pElement->GetBasicInformation()->GetName()));
        }
    }

    IUiView* pUIView = UiDocumentViewUtils::GetCurrentUiView();
    if (pUIView)
    {
        const IModelView* pModelView = pUIView->GetModelView();
        if (pModelView)
        {
            double topOffset = 0.0, cutOffset = 0.0, bottomOffset = 0.0, deepOffset = 0.0;
            bool hasTopPlane = pModelView->GetTopPlaneOffset(topOffset);
            bool hasCutPlane = pModelView->GetCutPlaneOffset(cutOffset);
            bool hasBottomPlane = pModelView->GetBottomPlaneOffset(bottomOffset);
            bool hasDeepPlane = pModelView->GetDeepPlaneOffset(deepOffset);

            if (hasTopPlane && hasCutPlane && hasBottomPlane && hasDeepPlane)
            {
                std::vector<const ILevel*> allLevels = LevelUtils::GetLevelsFromLowToHigh(pDoc);
                if (allLevels.empty() == false)
                {
                    const ILevel* pLevel = allLevels.front();
                    if (pLevel)
                    {
                        ui.levelComboBox->setCurrentText(QString::fromStdWString(pLevel->GetOwnerElement()->GetBasicInformation()->GetName()));
                    }
                }
            }
        }
        else
        {
            std::vector<const ILevel*> allLevels = LevelUtils::GetLevelsFromLowToHigh(pDoc);
            if (allLevels.empty() == false)
            {
                const ILevel* pLevel = allLevels.front();
                if (pLevel)
                {
                    ui.levelComboBox->setCurrentText(QString::fromStdWString(pLevel->GetOwnerElement()->GetBasicInformation()->GetName()));
                }
            }
        }
    }


    std::set<UniIdentity> categoryUids;
    categoryUids.insert(BuiltInCategoryUniIdentities::BICU_POINT_FAMILY);
    Init(pDoc,categoryUids, currentLevelId);
}

NewInPlaceFamDlg::NewInPlaceFamDlg(const IDocument* pDoc, const std::set<UniIdentity> & instantiatedCategoryUids, const ElementId currentLevelId, QWidget * pParent)
    : QDialog(pParent)
{
    ui.setupUi(this);

    this->setWindowTitle(QString::fromStdWString(GBMP_TR(L"选择构件类别")));
    ui.famNameLineEdit->setVisible(false);
    ui.famNameLabel->setVisible(false);
    ui.levelLabel->setVisible(false);
    ui.levelComboBox->setVisible(false);

    Init(pDoc,instantiatedCategoryUids, currentLevelId);
}

void NewInPlaceFamDlg::Init(const IDocument* pDoc, const std::set<UniIdentity> & availableCategoryUids, const ElementId currentLevelId)
{
    // 禁止编辑
    ui.categoriesTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // 禁止横向滚动
    ui.categoriesTableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    // 设置图标大小
    ui.categoriesTableWidget->setIconSize(QSize(s_categoryTableIconSize, s_categoryTableIconSize));

    // 只允许单选
    ui.categoriesTableWidget->setSelectionBehavior(QAbstractItemView::SelectItems);
    ui.categoriesTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);

    // 隐藏表头
    ui.categoriesTableWidget->horizontalHeader()->setVisible(false);
    ui.categoriesTableWidget->verticalHeader()->setVisible(false);

    // 设置列数和列宽
    const int categoryCount = (int)s_categoriesCellData.size();
    m_categoryTableColumnCount = std::min(s_categoryTableColumnCount, categoryCount);
    ui.categoriesTableWidget->setColumnCount(m_categoryTableColumnCount);
    ui.categoriesTableWidget->horizontalHeader()->setStretchLastSection(true);
    ui.categoriesTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    // 设置行数和行高
    const int rowCount = (categoryCount + m_categoryTableColumnCount - 1) / m_categoryTableColumnCount;
    ui.categoriesTableWidget->setRowCount(rowCount);
    for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx)
    {
        ui.categoriesTableWidget->setRowHeight(rowIdx, s_categoryTableColumnHeight);
    }

    // 设置Cell的图片和文字
    int firstAvailableCategoryIdx = -1;
    for (int idx = 0; idx < categoryCount; ++idx)
    {
        const ICategory* pCategory = ICategoryLibrary::Get(const_cast<IDocument*>(pDoc))->GetCategory(s_categoriesCellData[idx].UniIdentity);
        DBG_WARN_AND_CONTINUE_UNLESS(pCategory, L"pCategory为空",L"GDMPLab",L"2024-03-30");
        OwnerPtr<QTableWidgetItem> opQTableWidgetItem = NEW_AS_OWNER_PTR(
            QTableWidgetItem,
            QIcon(QString::fromStdWString(s_categoriesCellData[idx].IconPath)),
            QString::fromStdWString(pCategory->GetName())
        );
        const int rowIdx = idx / m_categoryTableColumnCount;
        const int colIdx = idx % m_categoryTableColumnCount;

        // Cell是否可用取决于Category
        Qt::ItemFlags flags = opQTableWidgetItem->flags();
        if (std::find_if(availableCategoryUids.begin(), availableCategoryUids.end(), [idx](const UniIdentity& uniIdentity){
                return s_categoriesCellData[idx].UniIdentity == uniIdentity;
            }) != availableCategoryUids.end())
        {
            opQTableWidgetItem->setFlags(flags | Qt::ItemIsEnabled);
            if (-1 == firstAvailableCategoryIdx)
            {
                firstAvailableCategoryIdx = idx;
            }
        }
        else
        {
            opQTableWidgetItem->setFlags(flags & ~Qt::ItemIsEnabled);
        }

        ui.categoriesTableWidget->setItem(rowIdx, colIdx, TRANSFER_OWNERSHIP_TO_THIRD_PARTY(opQTableWidgetItem));
    }

    // 设置默认选中Cell为用户上次选中的类别；如果上次没选择，则选择第一个可选的
    int defaultCategoryIdx = FindIndexOfCategory(s_lastCategoryUid);
    if (-1 == defaultCategoryIdx)
    {
        defaultCategoryIdx = firstAvailableCategoryIdx;
    }

    if (-1 == defaultCategoryIdx)
    {
        m_exchangeData.CategoryUid = UniIdentity::InvalidUid;

        ui.okPushButton->setEnabled(false);
    }
    else
    {
        const int row = defaultCategoryIdx / m_categoryTableColumnCount;
        const int col = defaultCategoryIdx % m_categoryTableColumnCount;
        ui.categoriesTableWidget->setCurrentCell(row, col);
        ui.categoriesTableWidget->setFocus(Qt::MouseFocusReason);

        m_exchangeData.CategoryUid = s_categoriesCellData[defaultCategoryIdx].UniIdentity;

        ui.okPushButton->setEnabled(true);
    }

    // 连接信号与信号槽
    connect(ui.okPushButton, SIGNAL(clicked()), this, SLOT(OnOkButtonPressed()));
    connect(ui.cancelPushButton, SIGNAL(clicked()), this, SLOT(OnCancelButtonPressed()));
    connect(ui.categoriesTableWidget, SIGNAL(cellClicked(int, int)), this, SLOT(OnCategoryCellClicked(int, int)));

    // 工作楼层
    std::vector<IElement*> pAllLevels = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION);
    FOR_EACH_INDEX(index, pAllLevels)
    {
        // 可能是LinkProxyElement，这里只处理主文档的GenericElement
        if (IGenericElement* pGenericElement = quick_cast<IGenericElement>(pAllLevels.at(index)))
        {
            ILevel* pLevel = quick_cast<ILevel>(pGenericElement->GetExternalObject());
            DBG_WARN_AND_RETURN_VOID_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");

            ElementId levelId = pLevel->GetOwnerElement()->GetElementId();
            std::wstring name = pLevel->GetOwnerElement()->GetBasicInformation()->GetName();
            ui.levelComboBox->addItem(QString::fromStdWString(name), QVariant(levelId.AsInt64()));
            // 工作楼层ComboBox显示当前的工作楼层
            if (currentLevelId == levelId)
            {
                ui.levelComboBox->setCurrentIndex(index);
            }
        }
    }
}

void NewInPlaceFamDlg::OnOkButtonPressed()
{
    m_exchangeData.FamName = ui.famNameLineEdit->text().toStdWString();
    m_exchangeData.WorkingLevelName = ui.levelComboBox->currentText().toStdWString();
    m_exchangeData.WorkLevelId = static_cast<ElementId>(ui.levelComboBox->currentData().toLongLong());
    s_lastCategoryUid = m_exchangeData.CategoryUid;
    QDialog::accept();
}

void NewInPlaceFamDlg::OnCancelButtonPressed()
{
    QDialog::reject();
}

void NewInPlaceFamDlg::OnCategoryCellClicked(int row, int column)
{
    const int idx = row * m_categoryTableColumnCount + column;
    GBMP_ASSERT(idx >= 0 && idx < (int)s_categoriesCellData.size());
    m_exchangeData.CategoryUid = s_categoriesCellData[idx].UniIdentity;
}
